import cv2
import numpy as np
from paddleocr import PaddleOCR
import pyttsx3
import difflib
import re
import threading
import time
import tkinter as tk
from PIL import Image, ImageTk, ImageDraw, ImageFont, ImageOps
from collections import OrderedDict
import multiprocessing as mp

class StoryReader:
    def __init__(self):
        # 配置参数集中管理
        self.config = {
            'frame_interval': 0.3,
            'similarity_threshold': 0.7,
            'min_contour_area': 2500,
            # 'ocr': {
            #     'use_angle_cls': True,
            #     'show_log': False
            # },
            'ocr': {
                'use_angle_cls': True,  # 必须关闭方向分类
                'lang': 'ch',            # 中英文混合识
                'show_log': False,
                'use_gpu': False,         # macOS必须关闭GPU加速
                'rec_image_shape': '3, 48, 640'  # 适合中英文的识别尺寸
            },
            'tts': {
                'rate': 150,
                'volume': 0.9
            }
        }
        
        # 初始化OCR引擎
        self.ocr = PaddleOCR(**self.config['ocr'])

        #初始化ytts引擎
        # 添加语音合成进程管理
        self.tts_queue = mp.Queue(maxsize=3)  # 限制队列大小防止堆积
        self.tts_process = None
        self._init_tts_process()

        # 内容管理
        self.last_text = ""
        self.ocr_cache = OrderedDict()
        self.cache_size = 50
        self.cache_hits = 0
        self.cache_misses = 0
        
        # 视频输入
        self.current_frame = None
        
        # 运行控制
        self.running = False
        self.last_process_time = 0
        self.processing = False
        self.start_time = time.time()
        
        # 系统状态监控
        self.system_status = {
            'camera': "未连接",
            'ocr': "待机",
            'tts': "静音"
        }

        # 初始化摄像头
        self.init_camera()
    
        # GUI初始化
        self.init_gui()

        # 启动处理线程
        threading.Thread(target=self.process_loop, daemon=True).start()

        self.root.mainloop()

    def init_camera(self):
        """增强的摄像头初始化"""
        if hasattr(self, 'cap') and self.cap.isOpened():
            self.cap.release()
        
        max_retries = 3
        for i in range(max_retries):
            try:
                self.cap = cv2.VideoCapture(0)
                if self.cap.isOpened():
                    self.system_status['camera'] = "已连接"
                    return
            except Exception as e:
                print(f"摄像头初始化尝试 {i+1} 失败: {str(e)}")
            time.sleep(1)
        self.system_status['camera'] = "连接失败"
        raise RuntimeError("摄像头初始化失败")

    def init_gui(self):
        """优化后的GUI布局"""
        self.root = tk.Tk()
        self.root.title("智能故事阅读器")
        self.root.geometry("600x400")
        self.root.protocol("WM_DELETE_WINDOW", self.on_close)
        
        # 主框架
        main_frame = tk.Frame(self.root)
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 视频显示区域
        self.video_label = tk.Label(main_frame, bd=1, relief="groove")
        self.video_label.grid(row=0, column=0, sticky="nsew", pady=5)
        
        # 状态栏
        self.status_label = tk.Label(
            main_frame,
            height=1,
            font=("楷体", 12),
            anchor="w",
            bg="#333",
            fg="white"
        )
        self.status_label.grid(row=1, column=0, sticky="ew", pady=2)
        
        # 信息显示面板
        info_frame = tk.Frame(main_frame, bg="#eee", height=80)
        info_frame.grid(row=2, column=0, sticky="ew", pady=5)
        
        self.info_text = tk.Text(
            info_frame,
            wrap=tk.WORD,
            height=4,
            font=("宋体", 10),
            bg="#f8f8f8"
        )
        self.info_text.pack(fill=tk.BOTH, expand=True)
        
        # 控制面板
        control_frame = tk.Frame(main_frame, bg="#f0f0f0")
        control_frame.grid(row=3, column=0, sticky="sew")
        
        # 控制按钮
        self.control_btn = tk.Button(
            control_frame,
            text="启动阅读",
            command=self.toggle_processing,
            font=("黑体", 14),
            bg="#4CAF50",
            fg="white",
            activebackground="#45a049",
            width=15
        )
        self.control_btn.pack(pady=10, side=tk.RIGHT, padx=20)
        
        # 布局配置
        main_frame.rowconfigure(0, weight=3)
        main_frame.rowconfigure(1, weight=0)
        main_frame.rowconfigure(2, weight=1)
        main_frame.rowconfigure(3, weight=0)
        main_frame.columnconfigure(0, weight=1)

        threading.Thread(target=self.update_gui_loop, daemon=True).start()

    def _init_tts_process(self):
        """初始化语音合成进程"""
        if self.tts_process and self.tts_process.is_alive():
            self.tts_process.terminate()
        
        # macOS需要使用spawn方式
        if mp.get_start_method(allow_none=True) != 'spawn':
            mp.set_start_method('spawn')
        
        self.tts_process = mp.Process(
            target=self._tts_worker,
            args=(self.tts_queue, self.config['tts']),
            daemon=True
        )
        self.tts_process.start()

    @staticmethod
    def _tts_worker(queue, config):
        """独立进程中的语音合成工作器"""
        import pyttsx3
        engine = pyttsx3.init()
        engine.setProperty('rate', config['rate'])
        engine.setProperty('volume', config['volume'])
        
        while True:
            try:
                if queue.empty():
                    time.sleep(0.1)
                    continue
                text = queue.get(timeout=5)  # 设置超时防止永久阻塞
                if text == "TERMINATE":
                    break
                engine.say(text)
                engine.runAndWait()
            except (mp.TimeoutError, KeyboardInterrupt):
                continue
            except Exception as e:
                print(f"语音合成异常: {str(e)}")
        
        engine.stop()
        print("语音合成进程已退出")

    def update_info_display(self):
        """更新信息显示面板"""
        info_content = f"""摄像头状态: {self.system_status['camera']} | OCR状态: {self.system_status['ocr']}
最新识别内容: {self.last_text[:20]}{'...' if len(self.last_text)>20 else ''}
缓存命中率: {self.get_cache_hit_rate():.1%} | 系统运行时间: {time.time() - self.start_time:.1f}秒"""
        self.info_text.config(state=tk.NORMAL)
        self.info_text.delete(1.0, tk.END)
        self.info_text.insert(tk.END, info_content)
        self.info_text.config(state=tk.DISABLED)

    def get_cache_hit_rate(self):
        total = self.cache_hits + self.cache_misses
        return self.cache_hits / total if total > 0 else 0

    def update_gui_loop(self):
        """独立线程中的GUI更新循环"""
        while True:
            self.update_gui()
            time.sleep(0.1)

    # def update_gui(self):
    #     if self.current_frame is None:
    #         placeholder = np.zeros((480, 640, 3), dtype=np.uint8)
    #         img = Image.fromarray(cv2.cvtColor(placeholder, cv2.COLOR_BGR2RGB))
    #     else:
    #         display_frame = self.draw_status_overlay(self.current_frame.copy())
    #         img = Image.fromarray(cv2.cvtColor(display_frame, cv2.COLOR_BGR2RGB))
        
    #     imgtk = ImageTk.PhotoImage(image=img)
    #     self.video_label.imgtk = imgtk
    #     self.video_label.configure(image=imgtk)
    def update_gui(self):
        """优化后的界面刷新方法"""
        if self.current_frame is not None:
            display_frame = self.current_frame.copy()    
            # 获取视频标签的当前尺寸
            label_width = self.video_label.winfo_width()
            label_height = self.video_label.winfo_height()
            
            # 跳过无效尺寸
            if label_width <= 0 or label_height <= 0:
                return
            
            # 计算缩放比例
            h, w = display_frame.shape[:2]
            ratio = min(label_width/w, label_height/h)
            new_size = (int(w*ratio), int(h*ratio))
            
            # 缩放并转换图像
            display_frame = cv2.resize(display_frame, new_size)
            img = Image.fromarray(cv2.cvtColor(display_frame, cv2.COLOR_BGR2RGB))
            imgtk = ImageTk.PhotoImage(image=img)
            
            # 更新显示
            self.video_label.imgtk = imgtk
            self.video_label.configure(image=imgtk)

    def process_loop(self):
        """优化后的主处理循环"""
        while True:
            self.system_status['camera'] = "运行中" if self.cap.isOpened() else "断开"
            self.update_info_display()
            
            if not self.running:
                time.sleep(0.2)
                continue
            
            if self.processing:
                time.sleep(0.2)
                continue
                
            self.processing = True
            self.last_process_time = time.time()

            try:
                ret, frame = self.cap.read()
                if ret:
                    self.current_frame = frame
                    self.process_frame(frame)
                else:
                    print("摄像头读取失败，尝试重新初始化")
                    self.reinit_system()
            except Exception as e:
                print(f"帧处理异常: {str(e)}")
                self.reinit_system()
            finally:
                self.processing = False

    def draw_status_overlay(self, frame):
        """在画面绘制状态信息"""
        y = 30
        cv2.putText(frame, "系统状态:", (10, y), 
                cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1, cv2.LINE_AA)
        
        fps = int(1 / (time.time() - self.last_process_time)) if self.last_process_time > 0 else 0
        cv2.putText(frame, f"FPS: {fps}", (frame.shape[1]-120, 30),
                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1, cv2.LINE_AA)
        
        return frame

    def process_frame(self, frame):
        """优化后的帧处理方法"""
        try:
            cropped = self.preprocess_image(frame)
            if cropped is None:
                return
                
            text = self.recognize_text(cropped)
            
            if not text and not self.is_new_content(text):
                return
            
            print(f"识别结果: {text}")
                
            # #在摄像头上显示识别结果
            # cv2.putText(frame, text, (10, 60),
            #         cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2, cv2.LINE_AA)
            self.last_text = text
            
            self.synthesize_speech(text)
         
        except Exception as e:
            print(f"处理异常: {str(e)}")
            self.reinit_system()

    def preprocess_image(self, frame):
        """增强的图像预处理"""
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        edges = cv2.Canny(gray, 50, 150)
        contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        
        max_area = 0
        best_rect = (0,0,0,0)
        min_area = self.config['min_contour_area']
        
        for cnt in contours:
            x, y, w, h = cv2.boundingRect(cnt)
            area = w * h
            if area > max_area and area > min_area:
                max_area = area
                best_rect = (x, y, w, h)
        
        x, y, w, h = best_rect
        return frame[y:y+h, x:x+w] if max_area > 0 else None

    def recognize_text(self, image):
        """优化的OCR文字识别方法"""
        if image is None or image.size == 0:
            return ""
        
        try:
            self.system_status['ocr'] = "处理中"
            gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
            gray = cv2.medianBlur(gray, 3)
            gray = cv2.equalizeHist(gray)
            
            img_hash = cv2.img_hash.averageHash(gray)[0].tobytes()
            
            if img_hash in self.ocr_cache:
                self.cache_hits += 1
                self.ocr_cache.move_to_end(img_hash)
                return self.ocr_cache[img_hash]
            
            self.cache_misses += 1
            result = self.ocr.ocr(image, cls=True)

            # 修复None判断
            if not result or not result[0] or not isinstance(result, list):
                return ""
            
            text = ' '.join([line[1][0] for line in result[0]]) if result else ""
            
            if len(self.ocr_cache) >= self.cache_size:
                self.ocr_cache.popitem(last=False)
            self.ocr_cache[img_hash] = text
            
            self.system_status['ocr'] = "完成"
            return text
            
        except Exception as e:
            print(f"OCR识别失败: {str(e)}")
            self.system_status['ocr'] = "失败"
            return ""

    # def recognize_text(self, image):
    #     """修复后的OCR文字识别方法"""
    #     if image is None or image.size == 0:
    #         return ""
        
    #     try:
    #         self.system_status['ocr'] = "处理中"
    #         gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    #         gray = cv2.medianBlur(gray, 3)
    #         gray = cv2.equalizeHist(gray)
            
    #         img_hash = cv2.img_hash.averageHash(gray)[0].tobytes()
            
    #         if img_hash in self.ocr_cache:
    #             self.cache_hits += 1
    #             self.ocr_cache.move_to_end(img_hash)
    #             return self.ocr_cache[img_hash]
            
    #         self.cache_misses += 1
    #         result = self.ocr.ocr(image)
            
    #         # 修复None判断
    #         if not result or not isinstance(result, list):
    #             return ""
            
    #         # 安全访问结果
    #         text_lines = []
    #         for line in result:
    #             if line and len(line) >= 2:
    #                 text_lines.append(line[1][0])
            
    #         text = ' '.join(text_lines) if text_lines else ""
            
    #         if len(self.ocr_cache) >= self.cache_size:
    #             self.ocr_cache.popitem(last=False)
    #         self.ocr_cache[img_hash] = text
            
    #         self.system_status['ocr'] = "完成"
    #         return text
            
    #     except Exception as e:
    #         print(f"OCR识别失败: {str(e)}")
    #         self.system_status['ocr'] = "失败"
    #         return ""
        
    def synthesize_speech(self, text):
            """优化的语音合成方法"""
            if not text or self.tts_queue.full():
                return
            
            try:
                # 先尝试清空旧消息
                while not self.tts_queue.empty():
                    self.tts_queue.get_nowait()
                
                self.tts_queue.put(text, block=False)
            except:
                # 队列已满时跳过当前内容
                pass

    # def synthesize_speech(self, text):
    #     """优化的语音合成方法"""
    #     if not text:
    #         return
        
    #     def tts_task():
    #         try:
    #             if not hasattr(self, 'tts_engine'):
    #                 self.tts_engine = pyttsx3.init()
    #                 self.tts_engine.setProperty('rate', self.config['tts']['rate'])
    #                 self.tts_engine.setProperty('volume', self.config['tts']['volume'])
                
    #             self.tts_engine.say(text)
    #             self.tts_engine.runAndWait()
    #             self.system_status['tts'] = "正常"
    #         except Exception as e:
    #             print(f"语音合成失败: {str(e)}")
    #             self.system_status['tts'] = "故障"
        
    #     threading.Thread(target=tts_task, daemon=True).start()

    # def is_new_content(self, text):
    #     """改进的内容更新检查"""
    #     chinese_text = ''.join([c for c in text if '\u4e00' <= c <= '\u9fff'])
    #     if len(chinese_text) < 3:
    #         return False
            
    #     similarity = difflib.SequenceMatcher(None, self.last_text, chinese_text).ratio()
    #     if similarity < self.config['similarity_threshold']:
    #         self.last_text = chinese_text
    #         return True
    #     return False

    def is_new_content(self, text):
        """支持中英文的更新检查"""
        # 保留中英文字符和基本标点
        clean_text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z\s,.?!]', '', text)
        
        # 双阈值检查
        length_threshold = 5 if any('\u4e00' <= c <= '\u9fff' for c in clean_text) else 10
        
        if len(clean_text) < length_threshold:
            return False
            
        similarity = difflib.SequenceMatcher(
            None, 
            self.last_text.lower(),  # 英文不区分大小写
            clean_text.lower()
        ).ratio()
        
        if similarity < self.config['similarity_threshold']:
            self.last_text = clean_text
            return True
        return False

    def reinit_system(self):
        """系统重初始化"""
        if hasattr(self, 'cap') and self.cap.isOpened():
            self.cap.release()
        self.init_camera()
        self.ocr = PaddleOCR(**self.config['ocr'])

    def toggle_processing(self):
        """改进的状态切换方法"""
        self.running = not self.running
        
        if self.running:
            try:
                self.init_camera()
            except RuntimeError as e:
                print(str(e))
                self.running = False
                return
        else:
            if self.cap.isOpened():
                self.cap.release()
        
        btn_text = "暂停阅读" if self.running else "继续阅读"
        self.control_btn.config(
            text=btn_text,
            bg="#f44336" if self.running else "#4CAF50"
        )
        
        # if self.running:
        #     self.update_gui()

    def on_close(self):

        """关闭时清理进程"""
        if self.tts_process and self.tts_process.is_alive():
            self.tts_queue.put("TERMINATE")
            self.tts_process.join(timeout=2)
            if self.tts_process.is_alive():
                self.tts_process.terminate()

        """处理窗口关闭事件"""
        self.running = False
        if hasattr(self, 'cap') and self.cap.isOpened():
            self.cap.release()
        # if hasattr(self, 'tts_engine'):
        #     self.tts_engine.stop()
        self.root.destroy()

    def draw_rectangle(self, frame, pos):
        cv2.line(frame, (int(pos[0][0][0]), int(pos[0][0][1])), (int(pos[0][1][0]), int(pos[0][1][1])), (0, 255, 0), 1, 4)
        cv2.line(frame, (int(pos[0][1][0]), int(pos[0][1][1])), (int(pos[0][2][0]), int(pos[0][2][1])), (0, 255, 0), 1, 4)
        cv2.line(frame, (int(pos[0][2][0]), int(pos[0][2][1])), (int(pos[0][3][0]), int(pos[0][3][1])), (0, 255, 0), 1, 4)
        cv2.line(frame, (int(pos[0][3][0]), int(pos[0][3][1])), (int(pos[0][0][0]), int(pos[0][0][1])), (0, 255, 0), 1, 4)
    
    def cv2ImgAddText(self, img, text, left, top, textColor, textSize):
        if (isinstance(img, np.ndarray)):  # 判断是否OpenCV图片类型
            img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
        # 创建一个可以在给定图像上绘图的对象
        # fontStyle = ImageFont.truetype(
        #     "font/simsun.ttc", textSize, encoding="utf-8")
        # txt = Image.new('L', (100, 100))
        # d = ImageDraw.Draw(txt)
        # d.text((0, 0), text, font=fontStyle, fill=255)
        # w = txt.rotate(17.5, expand=1)
        # img.paste(ImageOps.colorize(w, (0,0,0), (255,255,255)), (left, top),  w)
        draw = ImageDraw.Draw(img)
        # 字体的格式
        # fontStyle = ImageFont.truetype(
        #     "font/simsun.ttc", textSize, encoding="utf-8")
        # 绘制文本
        draw.text((left, top), text, textColor)
        # 转换回OpenCV格式
        return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)

if __name__ == "__main__":
    StoryReader()
